
#include "rtmp_connection.h"
#include "rtmp_message.h"
#include "rtmp_parser.h"
#include "rtmp_resource.h"
#include <cstdint>
#include <cstring>
#include <ctime>
#include <endian.h>
#include <memory>
#include <vector>

RtmpConnection::RtmpConnection(std::shared_ptr<Connection> conn)
{
    m_conn = conn;
    m_state = EmRtmpConnectInit;
    m_is_push_role = 1;
}

RtmpConnection::RtmpConnection(std::shared_ptr<Client> client, bool test)
{
    m_client = client;
    m_state  = EmRtmpConnectInit;
    m_is_push_role = 0;
}

RtmpConnection::~RtmpConnection()
{
    TRACE("release rtmp connection");
}

void RtmpConnection::SendRespMsg(std::vector<std::shared_ptr<RtmpMessage>> msg_vec)
{
    for (auto & data : msg_vec)
    {
        SendRespMsg(data);
    }
}

void RtmpConnection::SendRespMsg(std::shared_ptr<RtmpMessage> msg)
{

    m_rtmp_msg.ComposeRtmpMessage(msg);
    /* int chunk_size = m_rtmp_msg.ChunkBlockSize(); */
    /* TRACE("chunk block size : {}", chunk_size); */
    std::vector<uint8_t> data;
    m_rtmp_msg.GetData(data);
    SendData(&*data.begin(), data.size());

}

void RtmpConnection::Resource(std::shared_ptr<RtmpMediaResource> resource)
{
    m_resource = resource;
}


std::shared_ptr<RtmpMediaResource> RtmpConnection::Resource()
{
    return m_resource;
}

RtmpConnectState RtmpConnection::State()
{
    return m_state;
}

void RtmpConnection::UpdateState(RtmpConnectState state)
{
    m_state = state;
}

int RtmpConnection::ProcessHandShake(const uint8_t * data, int length)
{
    int result = 0;
    switch(m_state)
    {
        case EmRtmpConnectInit:
        {
            if (length == 1)
            {
                uint8_t version = 0x03;
                SendData(&version, sizeof(version));
                result = 1;
            }
            else if (length == 1536)
            {
                SendData(data, length);
                m_state = EmRtmpConnectRecvC0C1;
            }
            else if (length == 1537)
            { 
                uint32_t now_time = time(nullptr);
                now_time =  htobe32(now_time);
                memcpy((uint8_t*)(data + 1), &now_time, sizeof(now_time));
                memset((uint8_t*)(data + 5), 0, 4);
                SendData(data, length);
                SendData(data + 1, length - 1);
                result = length;
                m_state = EmRtmpConnectRecvC0C1;
            }
            else
            {
                /* result = length; */
            }
            break;
        }
        case EmRtmpConnectHandShake:
        {
        
        }
        case EmRtmpConnectSendC0C1:
        {
            if (length == 1)
            {
                     
            }
            else if (length == 1536)
            {
                /* m_state = EmRtmpConnectRecvC0C1; */ 
                /* result  = length; */
            } 
            else if (length == 1537)
            {
                /* m_state = EmRtmpConnectRecvC0C1; */
            }
            else if (length >= 1536 * 2 + 1)
            {
                const uint8_t * c2 = (data + 1 + 1536);
                SendData(c2, 1536);
                m_state = EmRtmpConnectInitFinish;
                result  = 1536 * 2 + 1;
            }
            else
            {
                result = length;
            }
            break;
        }
        case EmRtmpConnectRecvC0C1:
        { 
            m_state = EmRtmpConnectInitFinish;
            result = length;
            break;
        }
        default:
            break;
    }
    return result;
}

int RtmpConnection::PushRtmpData(const uint8_t * data, int length)
{
    if (m_state <= EmRtmpConnectRecvC0C1)
    {
        int resul = ProcessHandShake(data, length);
        return resul;
    }
    else
    {
        int use_length = 0;
        int number = m_rtmp_msg.PushDataToBuffer(data, length, use_length);
        if (number)
        {
            ProcessMsg();
        }
        TRACE("result number : {} and remove size : {}", number, use_length);
        return use_length;
    }
}

void RtmpConnection::ProcessMsg()
{
    std::shared_ptr<RtmpMessage> msg;
    while((msg = m_rtmp_msg.PopMsg()))
    {
        m_rtmp_proc.Process(shared_from_this(), msg);
        msg = nullptr;
    }
}

void RtmpConnection::SendData(const uint8_t * data, int length)
{
    if (m_conn)
    {
        m_conn->SendData(data, length);
    }
    if (!m_is_push_role && m_client)
    {
        m_client->SendData(data, length);
    }
}

void RtmpConnection::ReleaseStream()
{
    if (m_resource)
    {
        m_resource->RemoveConn(shared_from_this());
        m_resource = nullptr;
    }
}

int64_t RtmpConnection::Index()
{
    int64_t addr = (int64_t)((int64_t*)this);
    return addr;
}

void RtmpConnection::Remove()
{
    if (m_resource)
    {
        m_resource->RemoveConn(shared_from_this());
        m_resource = nullptr;
    }
}
